package com.iwhalecloud.citybrain.flow.platform.manager.application.service;

import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.BeanCopierUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.ModelUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.model.NextNodeCandidateTaskDTO;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.model.NodeCandidateTaskDTO;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.userentity.UserInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowNextNodeCandidateRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowProcessInstanceTraceRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowTaskCandidateSettingsRepository;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.SubProcess;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.*;

@Service
public class NodeCandidateService extends BaseProcessService{

    @Autowired
    private FlowNextNodeCandidateRepository flowNextNodeCandidateRepository;

    @Autowired
    private TaskService taskService;

    @Autowired
    private UserService userService;

    @Autowired
    private FlowTaskCandidateSettingsRepository flowTaskCandidateSettingsRepository;

    @Autowired
    private FlowProcessInstanceTraceRepository flowProcessInstanceTraceRepository;

    @Autowired
    private RepositoryService repositoryService;

    public void setAssigneeForNode(NextNodeCandidateTaskDTO nextNodeCandidateTaskDTO) throws Exception {
        String taskId = nextNodeCandidateTaskDTO.getTaskId();

        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String processInstanceId = task.getProcessInstanceId();

        //先找到当前节点的executionId
        String currentExecutionId = getCurrentExecutionId(task);
        String parentExecutionId = null;
        Execution execution = runtimeService.createExecutionQuery().executionId(currentExecutionId).singleResult();
        if (Objects.isNull(execution)){
            throw new Exception(String.format("根据[%s]获取的execution为空",currentExecutionId));
        }

        ExecutionEntity subProcessInstanceExecution = getSubProcessInstanceExecution(currentExecutionId);
        if (Objects.nonNull(subProcessInstanceExecution)){
            parentExecutionId = subProcessInstanceExecution.getId();
        }else{
            parentExecutionId = processInstanceId;
        }

        /*boolean isMulitTask = FlowUtils.getInstance().isMultiTaskActivity(processInstanceId,task.getTaskDefinitionKey());
        if (isMulitTask){
            Execution multiRootExecution = runtimeService.createExecutionQuery().executionId(execution.getParentId()).singleResult();
            if (Objects.isNull(multiRootExecution)){
                throw new Exception(String.format("根据[%s]获取的MultiRootExecution为空",execution.getParentId()));
            }
            //会签根执行的parentId，即会签节点所在流程的执行
            //如果是子流程的会签，那么parentExecutionId 为当前子流程实例的主执行
            //如果非子流程，则为当前分支上的主执行（并行网关也存在）
            parentExecutionId = multiRootExecution.getParentId();

        }else{
            parentExecutionId = execution.getParentId();
        }*/

        if (StringUtils.isBlank(parentExecutionId)){
            throw new Exception(String.format("当前任务[%s]的主执行为空，请联系管理员处理",taskId));
        }

        // 先删除当前节点已选中历史候选人记录
        for (NodeCandidateTaskDTO vo : nextNodeCandidateTaskDTO.getNodeCandidateTaskDTOList()) {
            List<ModelNextNodeCandidateSettings>  flowNextNodeCandidateList = flowNextNodeCandidateRepository.selectByProcessInstanceIdAndNodeCodeAndParentExecutionId(processInstanceId,
                    vo.getNodeCode(), parentExecutionId);
            if (flowNextNodeCandidateList!=null && flowNextNodeCandidateList.size()>0) {
                for (ModelNextNodeCandidateSettings candidate : flowNextNodeCandidateList) {
                    flowNextNodeCandidateRepository.deleteByPrimaryKey(candidate.getId());
                }
            }
        }
        // 查询流程实例对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        List<ModelTaskCandidateSettingsDomain> dataList = null;
        for (NodeCandidateTaskDTO vo : nextNodeCandidateTaskDTO.getNodeCandidateTaskDTOList()) {
            FlowNextNodeCandidate candidate = new FlowNextNodeCandidate();
            candidate.setNodeCode(vo.getNodeCode());
            candidate.setProcessInstanceId(processInstanceId);
            candidate.setDataType(vo.getDataType());
            candidate.setName(vo.getName());
            candidate.setCode(vo.getCode());
            candidate.setFromNodeCode(vo.getFromNodeCode());
            candidate.setParentExecutionId(parentExecutionId);
            // 这里保存当前任务ID为了在指定下一节点候选人类型时关系或自定义函数时,在调用resolveCandidate方法解析时使用
            candidate.setTaskId(taskId);
            candidate.saveFlowNextNodeCandidate();

            if (processInstance!=null) {
                // 更新流程任务处理人是否已进行了设置候选人操作标识，isPick=0表示未设置候选人（前端弹框选人），isPick=1表示已设置候选人（前端不弹框选人）
                dataList = flowTaskCandidateSettingsRepository.getModelTaskCandidateSettingsList(processInstance.getDeploymentId(), vo.getNodeCode(), "0");
                if (dataList!=null && dataList.size()>0) {
                    for (ModelTaskCandidateSettingsDomain mtcsd : dataList) {
                        ModelTaskCandidateSettings entity = new ModelTaskCandidateSettings();
                        BeanCopierUtils.copyProperties(mtcsd, entity);
                        entity.setIsPick("1");
                        entity.setUpdateTime(new Date());
                        flowTaskCandidateSettingsRepository.updateByPrimaryKeySelective(entity);
                    }
                }
            }
        }
    }

    /**
     * 根据executionId获取当前子流程实例的execution
     * @param executionId
     * @return
     */
    public ExecutionEntity getSubProcessInstanceExecution(String executionId) {
        if (StringUtils.isNotBlank(executionId)){
            ExecutionEntity executionEntity = (ExecutionEntity) runtimeService.createExecutionQuery().executionId(executionId).singleResult();
            if (Objects.nonNull(executionEntity)){
                FlowElement flowElement = repositoryService.getBpmnModel(executionEntity.getProcessDefinitionId()).getFlowElement(executionEntity.getActivityId());
                if (Objects.nonNull(flowElement) && (flowElement instanceof SubProcess)){
                    return executionEntity;
                }else {
                    return getSubProcessInstanceExecution(executionEntity.getParentId());
                }
            }
        }
        return null;
    }

    /**
     * 获取当前节点的executionId
     * 考虑加签和委派的情况，executionId可能为空，需要向上继续找
     * @param task
     * @return
     */
    public String getCurrentExecutionId(Task task){
        String currentExecutionId = task.getExecutionId();
        if (StringUtils.isBlank(currentExecutionId)){
            if (StringUtils.isNotBlank(task.getParentTaskId())){
                task = taskService.createTaskQuery().taskId(task.getParentTaskId()).singleResult();
                return getCurrentExecutionId(task);
            }
        }
        return currentExecutionId;
    }

    /**
     * 设置节点候选人列表
     *
     * @param nextNodeCandidateTaskDTO
     * @throws Exception
     */
    public void setCandidateUsersForNode(NextNodeCandidateTaskDTO nextNodeCandidateTaskDTO) throws Exception {
        if(CollectionUtils.isEmpty(nextNodeCandidateTaskDTO.getNodeCandidateTaskDTOList())) {
            return;
        }

        // 存储员工工号集合
        List<String> employeeCodes = new ArrayList<>();

        // 先删除当前节点已选中历史候选人记录
        for (NodeCandidateTaskDTO vo : nextNodeCandidateTaskDTO.getNodeCandidateTaskDTOList()) {
            List<ModelNextNodeCandidateSettings>  flowNextNodeCandidateList = flowNextNodeCandidateRepository.selectByProcessInstanceIdAndNodeCodeAndParentExecutionId(nextNodeCandidateTaskDTO.getProcessInstanceId(),
                    vo.getNodeCode(), nextNodeCandidateTaskDTO.getProcessInstanceId());
            if (flowNextNodeCandidateList!=null && flowNextNodeCandidateList.size()>0) {
                for (ModelNextNodeCandidateSettings candidate : flowNextNodeCandidateList) {
                    flowNextNodeCandidateRepository.deleteByPrimaryKey(candidate.getId());
                }
            }
            // 添加员工工号
            employeeCodes.add(vo.getCode());
        }

        // 为空直接返回
        if(CollectionUtils.isEmpty(employeeCodes)) {
            return;
        }

        // 批量查询用户信息
        List<UserInfo> userInfos = userService.getUserListByEmployeeCodeList(employeeCodes);

        // 批量查询出来的用户信息与员工编码匹配匹对之后,将其code设置成userId
        for (NodeCandidateTaskDTO vo : nextNodeCandidateTaskDTO.getNodeCandidateTaskDTOList()) {
            for (UserInfo userInfo : userInfos) {
                if(Objects.equals(vo.getCode(), userInfo.getEmployeeCode())) {
                    vo.setCode(userInfo.getUserCode());
                }
            }
        }

        // 1.根据流程实例ID获取整个流程信息
        FlowProcessInstanceTraceVo processInstanceTrace = flowProcessInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(nextNodeCandidateTaskDTO.getProcessInstanceId());
        if(Objects.isNull(processInstanceTrace)) {
            return;
        }

        // 2.获取整个流程中的每个节点编码
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstanceTrace.getProcessDefinitionId()).singleResult();

        // 获取流程资源
        InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName() + ".json");
        ProcessModelVo processModel = ModelUtils.getInstance().streamToModel(inputStream);

        // 获取整个流程上的所有节点编码
        List<String> allNodeCodes = new ArrayList<>();
        Map<String,List<LinkedHashMap>> candidateUserMap = new HashMap<>();
        if (processModel != null) {
            allNodeCodes = this.getAllNodeCodesByProcess(processModel, candidateUserMap);
        }

        // 3.筛选出未指定候选人的节点
        Set<String> setNodeCodes = new HashSet<>();
        for (NodeCandidateTaskDTO taskDTO : nextNodeCandidateTaskDTO.getNodeCandidateTaskDTOList()) {
            setNodeCodes.add(taskDTO.getNodeCode());
            setNodeCodes.add(taskDTO.getFromNodeCode());
        }

        // 存储未设置的节点编码
        Set<String> noSetNodeCodes = new HashSet<>();

        // 循环所有节点编码列表
        for (String nodeCode:allNodeCodes) {
            if(!setNodeCodes.contains(nodeCode)) {
                noSetNodeCodes.add(nodeCode);
            }
        }

        // 4.按照每个节点配置去解析对应的候选人列表
        if(CollectionUtils.isEmpty(noSetNodeCodes) ||
                MapUtils.isEmpty(candidateUserMap)) {
            return;
        }

        // 来源节点
        String fromNodeCode = "";

        // 取来源节点
        for (NodeCandidateTaskDTO vo : nextNodeCandidateTaskDTO.getNodeCandidateTaskDTOList()) {
            fromNodeCode = vo.getFromNodeCode();
            break;
        }

        // 组装未指定节点候选人数据集合
        List<NodeCandidateTaskDTO> noNodeCandidateTaskDTOS = new ArrayList<>();
        for (String nodeCode:noSetNodeCodes) {
            List<LinkedHashMap> candidateUsersMap = candidateUserMap.get(nodeCode);
            if(CollectionUtils.isEmpty(candidateUsersMap)) {
                continue;
            }

            // 循环遍历取出数值
            for(LinkedHashMap userMap : candidateUsersMap) {
                String type = String.valueOf(userMap.get("type"));
                String code = String.valueOf(userMap.get("code"));
                String name = String.valueOf(userMap.get("name"));
                // 排除是变量中获取的
                if(!Objects.equals("VAR", type)) {
                    NodeCandidateTaskDTO taskDTO = new NodeCandidateTaskDTO();
                    taskDTO.setNodeCode(nodeCode);
                    taskDTO.setDataType(type);
                    taskDTO.setName(name);
                    taskDTO.setCode(code);
                    taskDTO.setFromNodeCode(fromNodeCode);
                    noNodeCandidateTaskDTOS.add(taskDTO);
                }
            }
        }

        // 先删除当前节点已选中历史候选人记录
        for (NodeCandidateTaskDTO vo : noNodeCandidateTaskDTOS) {
            List<ModelNextNodeCandidateSettings>  flowNextNodeCandidateList = flowNextNodeCandidateRepository.selectByProcessInstanceIdAndNodeCodeAndParentExecutionId(nextNodeCandidateTaskDTO.getProcessInstanceId(),
                    vo.getNodeCode(), nextNodeCandidateTaskDTO.getProcessInstanceId());
            if (flowNextNodeCandidateList!=null && flowNextNodeCandidateList.size()>0) {
                for (ModelNextNodeCandidateSettings candidate : flowNextNodeCandidateList) {
                    flowNextNodeCandidateRepository.deleteByPrimaryKey(candidate.getId());
                }
            }
        }

        // 5.添加到设置候选人的集合列表里
        nextNodeCandidateTaskDTO.getNodeCandidateTaskDTOList().addAll(noNodeCandidateTaskDTOS);

        // 循环保存设置后续节点候选人
        for (NodeCandidateTaskDTO vo : nextNodeCandidateTaskDTO.getNodeCandidateTaskDTOList()) {
            FlowNextNodeCandidate candidate = new FlowNextNodeCandidate();
            candidate.setNodeCode(vo.getNodeCode());
            candidate.setProcessInstanceId(nextNodeCandidateTaskDTO.getProcessInstanceId());
            candidate.setDataType(vo.getDataType());
            candidate.setName(vo.getName());
            candidate.setCode(vo.getCode());
            candidate.setFromNodeCode(vo.getFromNodeCode());
            candidate.setParentExecutionId(nextNodeCandidateTaskDTO.getProcessInstanceId());
            candidate.saveFlowNextNodeCandidate();
        }
    }



    /**
     * 清除节点候选人信息
     *
     * @param nodeCodes
     * @param processInstanceId
     * @throws Exception
     */
    public void cleanNodeCandidateUsers(String processInstanceId,List<String> nodeCodes) throws Exception{
        if(CollectionUtils.isEmpty(nodeCodes)) {
            return;
        }

        // 删除当前节点已选中历史候选人记录
        for (String nodeCode : nodeCodes) {
            List<ModelNextNodeCandidateSettings>  flowNextNodeCandidateList = flowNextNodeCandidateRepository.selectByProcessInstanceIdAndNodeCodeAndParentExecutionId(processInstanceId,
                    nodeCode, processInstanceId);
            if (flowNextNodeCandidateList!=null && flowNextNodeCandidateList.size()>0) {
                for (ModelNextNodeCandidateSettings candidate : flowNextNodeCandidateList) {
                    flowNextNodeCandidateRepository.deleteByPrimaryKey(candidate.getId());
                }
            }
        }
    }
    /**
     * 获取整个流程上所有的节点编码
     *
     * @param processModel
     * @return
     */
    private List<String> getAllNodeCodesByProcess(ProcessModelVo processModel, Map<String,List<LinkedHashMap>> candidateUserMap) {
        List<String> nodeCodes = new ArrayList<>();
        List<Object> childShapes = processModel.getChildShapes();
        // 循环整个模型的树型结构
        for (Object childShape : childShapes) {
            // 获取形状
            LinkedHashMap childShapeMap = (LinkedHashMap)childShape;
            if(MapUtils.isEmpty(childShapeMap)) {
                return Collections.emptyList();
            }

            // 获取属性节点
            LinkedHashMap propertiesList = (LinkedHashMap) childShapeMap.get("properties");
            if(Objects.isNull(propertiesList)) {
                return Collections.emptyList();
            }

            // 节点编码
            String activityId = String.valueOf(propertiesList.get("overrideid"));

            // 节点的数据类型
            String flowType = String.valueOf(((LinkedHashMap)childShapeMap.get("stencil")).get("id"));
            if (!"SequenceFlow".equals(flowType) &&
                    !"StartNoneEvent".equals(flowType) &&
                    !"EndNoneEvent".equals(flowType)) {
                if (StringUtils.isNotEmpty(activityId)) {
                    nodeCodes.add(activityId);
                }

                // 任务类型节点才取出节点的候选人列表
                if (Objects.equals("UserTask", flowType)) {
                    List<LinkedHashMap> candidateUsersList = (List<LinkedHashMap>) propertiesList.get("candidateUsers");
                    candidateUserMap.put(activityId, candidateUsersList);
                }
            }
        }
        return nodeCodes;
    }
}
